package cn.maihe.elg.operation.supports.bank.service;

import cn.hutool.core.date.DatePattern;
import cn.maihe.elg.operation.config.ZtAccountInfo;
import cn.maihe.elg.operation.repository.entity.SpdBankNotice;
import cn.maihe.elg.operation.service.order.AcceptOrderInfoService;
import cn.maihe.elg.operation.service.order.SpdBankNoticeService;
import cn.maihe.elg.operation.supports.bank.BaseCmbBankTransService;
import cn.maihe.elg.operation.supports.bank.cmb.dto.CommonReq;
import cn.maihe.elg.operation.supports.bank.cmb.dto.CommonResp;
import cn.maihe.elg.operation.supports.bank.cmb.dto.TrsQryHead;
import cn.maihe.elg.operation.supports.bank.cmb.dto.TrsQryReq;
import cn.maihe.elg.operation.supports.bank.cmb.dto.TrsQryReqBody;
import cn.maihe.elg.operation.supports.bank.cmb.dto.TrsQryReqX1;
import cn.maihe.elg.operation.supports.bank.cmb.dto.TrsQryReqY1;
import cn.maihe.elg.operation.supports.bank.cmb.dto.TrsQryResp;
import cn.maihe.elg.operation.supports.bank.cmb.dto.TrsQryRespBody;
import cn.maihe.elg.operation.supports.bank.cmb.dto.TrsQryRespZ1;
import cn.maihe.elg.operation.supports.bank.cmb.dto.TrsQryRespZ2;
import cn.maihe.elg.operation.supports.bank.cmb.tradingmanager.base.constant.StringPool;
import cn.maihe.elg.operation.supports.bank.cmb.tradingmanager.pojo.AccountTrade;
import cn.maihe.elg.operation.supports.bank.cmb.util.CashServiceUtil;
import cn.maihe.elg.operation.supports.bank.enums.CmbMethod;
import cn.maihe.elg.operation.utils.DateUtil;
import cn.maihe.elg.operation.utils.ElgLogger;
import cn.maihe.elg.operation.utils.IdGenerate;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.github.pagehelper.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @Description 账务查询 7.账户交易信息查询trsQryByBreakPoint
 * @Author Alan
 */
@Service
@Slf4j
public class CmbBankAccountDetailService extends BaseCmbBankTransService<TrsQryReq, TrsQryResp> {

    @Resource
    private SpdBankNoticeService spdBankNoticeService;
    @Resource
    private AcceptOrderInfoService acceptOrderInfoService;
    @Resource
    private ZtAccountInfo ztAccountInfo;

    private AtomicBoolean running = new AtomicBoolean(false);

    @Override
    protected CmbMethod cmbMethod() {
        return CmbMethod.TrsQryByBreakPoint;
    }

    @Scheduled(cron = "${cn.maihe.elg.bank.cmb-bank.getAccountDetailCore}")
    public void scheduleCmbAccountDetail() {

        if (!"1".equals(cmbBankConfig.getOpenFlag())) {
            log.info("当前已关闭招商银行银企直连功能，暂无需开启定时拉取账户明细任务");
            return;
        }

        if (running.compareAndSet(false, true)) {
            long start = System.currentTimeMillis();
            ElgLogger.info(log, l -> l.info("定时拉取账户明细开始>>>"));
            try {
                //若是凌晨00:30前则需要同步昨天的记录，以防行内来账较迟
                LocalDateTime startDate = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0);
                if (LocalTime.now().isBefore(LocalTime.of(0, 30, 0))) {
                    startDate = startDate.minusDays(1);
                }
                LocalDateTime endDate = LocalDateTime.now();

                this.getAccountDetail(startDate, endDate, null, null);
                this.getSubAccountDetail();
            } catch (Exception e) {
                ElgLogger.error(log, l -> l.error("定时拉取账户明细异常:{}", e.getMessage(), e));
            } finally {
                running.set(false);
                long end = System.currentTimeMillis();
                ElgLogger.info(log, l -> l.info("定时拉取账户明细完成<<<耗时[" + (end - start) / 1000 + "S]"));
            }
        } else {
            ElgLogger.info(log, l -> l.info("定时拉取账户明细开始>>>已存在同步拉取任务，等待下一个定时任务重跑"));
        }
    }

    private void getSubAccountDetail(){
        String currentDate = DateUtil.getCurrentDate(DateUtil.FORMAT_TRADEDATE);
        try {
            List<AccountTrade> accountTrades = CashServiceUtil.findAllAccountTradeToday(null);
            for (AccountTrade accountTrade : accountTrades) {
                // 过滤非收入的流水
                if(!"C".equals(accountTrade.getTradeType())){
                    continue;
                }
                // 过滤默认子账户的流水
                if("0000000".equals(accountTrade.getSubAccount())){
                    continue;
                }
                if(!spdBankNoticeService.existsCoreSerNo(accountTrade.getSerialNumber())){
                    SpdBankNotice spdBankNotice = spdBankNoticeService.saveOrUpdate(buildXibBankNotice(currentDate, accountTrade));
                    acceptOrderInfoService.autoAuditWaitPayConfirm(spdBankNotice);
                }
            }
        } catch (Exception e) {
            log.error("子账号自动出函异常");
        }
    }

    private SpdBankNotice buildXibBankNotice(String currentDate,AccountTrade accountTrade) {
        String transTime = accountTrade.getTransTime();
        String serialNumber = accountTrade.getSerialNumber();
        SpdBankNotice spdBankNotice = new SpdBankNotice();
        spdBankNotice.setId(IdGenerate.getId());
        spdBankNotice.setNoticeDate(currentDate);
        spdBankNotice.setNoticeTime(transTime);
        spdBankNotice.setNoticeType("");
        spdBankNotice.setNoticeSerNo(serialNumber);
        spdBankNotice.setZoneCode("");
        spdBankNotice.setSecuNo("");
        spdBankNotice.setSecuName("");
        spdBankNotice.setMerchNo("");
        spdBankNotice.setMerchName("");
        spdBankNotice.setParentAcctNo(accountTrade.getBankAccount());
        spdBankNotice.setParentAcctName(accountTrade.getSubAccountName());
        spdBankNotice.setDepSerNo(serialNumber);
        spdBankNotice.setCoreSerNo(serialNumber);
        spdBankNotice.setCoreTransDate(currentDate);
        spdBankNotice.setCoreTransTime(transTime);
        spdBankNotice.setTransDate(currentDate);
        spdBankNotice.setTransTime(transTime);
        //这里存储对方账号类型
        spdBankNotice.setBusiType("");
        spdBankNotice.setTransAmt(accountTrade.getTransAmount());
        spdBankNotice.setAcctBankName("");
        spdBankNotice.setAcctBankNo("");
        spdBankNotice.setAcctName(accountTrade.getTransName());
        spdBankNotice.setAcctNo(accountTrade.getTransAccount());
        spdBankNotice.setAttach(accountTrade.getTransRemark());
        spdBankNotice.setRemark1(null);
        spdBankNotice.setRemark2(null);
        spdBankNotice.setRemark3(null);
        spdBankNotice.setGmtCreate(new Date());
        spdBankNotice.setGmtModified(new Date());
        String subAccount = accountTrade.getSubAccount();
        if(StringUtils.isNotBlank(subAccount) && StringUtils.isNotBlank(subAccount.trim()) && !subAccount.equals(StringPool.NULL)){
            spdBankNotice.setSubAccount(subAccount);
        }
        return spdBankNotice;
    }

    public boolean getAccountDetail(LocalDateTime startDate, LocalDateTime endDate, TrsQryReqY1 trsQryReqY1, TrsQryRespZ1 trsQryRespZ1) {
        CommonReq<TrsQryReq> req = new CommonReq<>();
        TrsQryReq trsQryReq = new TrsQryReq();
        TrsQryReqBody trsQryReqBody = new TrsQryReqBody();
        TrsQryReqX1 trsQryReqX1 = new TrsQryReqX1();
        trsQryReqX1.setCardNbr(cmbBankConfig.getAccNo());
        trsQryReqX1.setBeginDate(startDate.format(DateTimeFormatter.ofPattern(DatePattern.PURE_DATE_PATTERN)));
        trsQryReqX1.setEndDate(endDate.format(DateTimeFormatter.ofPattern(DatePattern.PURE_DATE_PATTERN)));
        trsQryReqX1.setLoanCode("C");
        if (trsQryRespZ1 != null) {
            trsQryReqX1.setQueryAcctNbr(trsQryRespZ1.getQueryAcctNbr());
        }
        trsQryReqBody.setTrsQryReqX1(new TrsQryReqX1[]{trsQryReqX1});
        if (trsQryReqY1 != null) {
            trsQryReqBody.setTrsQryReqY1(new TrsQryReqY1[]{trsQryReqY1});
        }
        trsQryReq.setBody(trsQryReqBody);

        TrsQryHead trsQryHead = new TrsQryHead();
        int BOUND_START = 1000000;
        int BOUND_END = 9000000;
        Random random = new Random();
        String reqid = new SimpleDateFormat("yyyyMMddHHmmssSSSS").format(new Date()) + (BOUND_START + random.nextInt(BOUND_END));
        trsQryHead.setReqId(reqid);
        trsQryHead.setFunCode(cmbMethod().getMethodId());
        trsQryHead.setUserId(cmbBankConfig.getUid());
        trsQryReq.setHead(trsQryHead);
        req.setRequest(trsQryReq);

        try {
            String response = this.sendCmbData(req);
            CommonResp<TrsQryResp> respDTO = JSON.parseObject(response, new TypeReference<CommonResp<TrsQryResp>>() {
            });
            if (respDTO == null || respDTO.getResponse() == null || respDTO.getResponse().getHead() == null) {
                throw new RuntimeException("账户响应报文为空");
            }
            trsQryHead = respDTO.getResponse().getHead();
            String respCode = trsQryHead.getResultCode();
            String respMsg = trsQryHead.getResultMsg();
            if ("SUC0000".equals(respCode) && respDTO.getResponse().getBody() != null) {
//                throw new RuntimeException("招商银行响应文头 respCode:" + respCode + " respMsg:" + respMsg);
                TrsQryRespBody trsQryRespBody = respDTO.getResponse().getBody();
                String ctnFlag = trsQryRespBody.getTrsQryRespZ1()[0].getCtnFlag();
                if ("Y".equals(ctnFlag)) {
                    trsQryReqY1 = new TrsQryReqY1();
                    BeanUtils.copyProperties(trsQryRespBody.getTrsQryRespY1()[0], trsQryReqY1);
                    processAcctTranDtlInfo(respDTO);
                    return getAccountDetail(startDate, endDate, trsQryReqY1, trsQryRespZ1);
                } else {
                    processAcctTranDtlInfo(respDTO);
                    return true;
                }
            } else {
                throw new RuntimeException("招商银行响应报文状态码非成功[" + trsQryHead + ":" + respMsg + "]");
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void processAcctTranDtlInfo(CommonResp<TrsQryResp> trsQryRespCommonResp) {
        if (trsQryRespCommonResp.getResponse().getBody().getTrsQryRespZ2() == null) {
            return;
        }
        Arrays.stream(trsQryRespCommonResp.getResponse().getBody().getTrsQryRespZ2())
                .filter(o -> {
                    String virtualNbr = o.getVirtualNbr();
                    if(StringUtil.isNotEmpty(virtualNbr)){
                        if(StringUtil.isNotEmpty(virtualNbr.trim())){
                            return false;
                        }
                    }

                    boolean exists = spdBankNoticeService.existsCoreSerNo(o.getTransSequenceIdn());
                    log.info("账户明细核心流水号[{}]已存在，忽略该笔明细处理.", o.getTransSequenceIdn());
                    return !exists;
                }).map(o -> this.buildXibBankNotice(trsQryRespCommonResp, o)).forEach(o -> {
                    try {
                        saveAndAutoAuditWaitPayConfirm(o);
                    } catch (Exception e) {
                        ElgLogger.error(log, l -> l.error(e.getMessage(), e));
                    }

                });
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveAndAutoAuditWaitPayConfirm(SpdBankNotice spdBankNotice) {
        try {
            SpdBankNotice dbSpdBankNotice = spdBankNoticeService.saveOrUpdate(spdBankNotice);
            acceptOrderInfoService.autoAuditWaitPayConfirm(dbSpdBankNotice);
        } catch (Exception e) {
            throw new RuntimeException("拉取账户明细核查受理订单到账异常", e);
        }
    }

    /**
     * 核心流水号唯一：来账账号+交易金额+附言包含的标段编号的定位某笔受理订单号到账证明
     */
    public SpdBankNotice buildXibBankNotice(CommonResp<TrsQryResp> trsQryRespCommonResp, TrsQryRespZ2 trsQryRespZ2) {
        String transDate = trsQryRespZ2.getTransDate();
        String transTime = trsQryRespZ2.getTransTime();
        SpdBankNotice spdBankNotice = new SpdBankNotice();
        spdBankNotice.setId(IdGenerate.getId());
        spdBankNotice.setNoticeDate(transDate);
        spdBankNotice.setNoticeTime(transTime);
        spdBankNotice.setNoticeType("");
        spdBankNotice.setNoticeSerNo(trsQryRespZ2.getTransSequenceIdn());
        spdBankNotice.setZoneCode("");
        spdBankNotice.setSecuNo("");
        spdBankNotice.setSecuName("");
        spdBankNotice.setMerchNo("");
        spdBankNotice.setMerchName("");
        spdBankNotice.setParentAcctNo(ztAccountInfo.getAccNo());
        spdBankNotice.setParentAcctName(ztAccountInfo.getAccName());
        spdBankNotice.setDepSerNo(trsQryRespZ2.getTransSequenceIdn());
        spdBankNotice.setCoreSerNo(trsQryRespZ2.getTransSequenceIdn());
        spdBankNotice.setCoreTransDate(transDate);
        spdBankNotice.setCoreTransTime(transTime);
        spdBankNotice.setTransDate(transDate);
        spdBankNotice.setTransTime(transTime);
        //这里存储对方账号类型
        spdBankNotice.setBusiType("");
        spdBankNotice.setTransAmt(trsQryRespZ2.getTransAmount());
        spdBankNotice.setAcctBankName(trsQryRespZ2.getCtpBankName());
        spdBankNotice.setAcctBankNo("");
        spdBankNotice.setAcctName(trsQryRespZ2.getCtpAcctName());
        spdBankNotice.setAcctNo(trsQryRespZ2.getCtpAcctNbr());

        String businessText = trsQryRespZ2.getBusinessText();
        if (StringUtil.isNotEmpty(trsQryRespZ2.getBusinessText())){
            businessText = businessText.trim();
        }
        spdBankNotice.setAttach(StringUtil.isNotEmpty(businessText) ?
                trsQryRespZ2.getBusinessText() : trsQryRespZ2.getRemarkTextClt());
        spdBankNotice.setRemark1(null);
        spdBankNotice.setRemark2(null);
        spdBankNotice.setRemark3(null);
        spdBankNotice.setGmtCreate(new Date());
        spdBankNotice.setGmtModified(new Date());

        return spdBankNotice;
    }

    /**
     * 账户类型 string(2)
     * 00-客户入金 ==>客户入金通知	A1007
     * 01-非客户入金 非客户入金通知	A1010
     * 02-不明入金 不明入金通知	A1008
     * 03-客户出金 客户出金通知	A1004
     * 04-非客户出金 非客户出金通知	A1003
     * 05-调拨入金 内部资金调拨通知	A3003
     * 06-调拨出金 内部资金调拨通知	A3003
     */
    private static String getNoticeTypeByBookAcctType(String bookAcctType) {
        switch (bookAcctType) {
            case "00":
                return "A1007";
            case "01":
                return "A1010";
            case "02":
                return "A1008";
            case "03":
                return "A1004";
            case "04":
                return "A1003";
            case "05":
                return "A3003";
            case "06":
                return "A3003";
            default:
                return null;
        }
    }
}
